#include "drivers/pcap_adapter.h"

#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <windows.h>
#endif

#include <pcap.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#ifndef _WIN32
#include <pthread.h>
#include <unistd.h>
#else
static DWORD WINAPI recv_thread_func(LPVOID arg);
#endif

typedef struct {
	pcap_t* p;
	pcap_recv_cb cb;
	void* ctx;
	int running;
#ifdef _WIN32
	HANDLE thread;
#else
	pthread_t thread;
#endif
} pcap_handle_t;

static void pcap_dispatch_cb(u_char* user, const struct pcap_pkthdr* h, const u_char* bytes) {
	pcap_handle_t* ph = (pcap_handle_t*)user;
	if (ph && ph->cb) ph->cb(bytes, h->len, ph->ctx);
}

int pcap_adapter_open(const char* device, void** handle_out) {
	if (!device || !handle_out) return -1;
	char errbuf[PCAP_ERRBUF_SIZE] = {0};
	pcap_t* p = pcap_open_live(device, 65536, 1, 1000, errbuf);
	if (!p) return -1;
	pcap_handle_t* ph = (pcap_handle_t*)malloc(sizeof(pcap_handle_t));
	memset(ph,0,sizeof(*ph));
	ph->p = p;
	*handle_out = ph;
	return 0;
}

int pcap_adapter_send(void* handle, const uint8_t* pkt, size_t len) {
	if (!handle || !pkt) return -1;
	pcap_handle_t* ph = (pcap_handle_t*)handle;
	if (pcap_sendpacket(ph->p, pkt, (int)len) != 0) {
		return -1;
	}
	return 0;
}

int pcap_adapter_start_recv(void* handle, pcap_recv_cb cb, void* ctx) {
	if (!handle || !cb) return -1;
	pcap_handle_t* ph = (pcap_handle_t*)handle;
	ph->cb = cb;
	ph->ctx = ctx;
	ph->running = 1;
#ifdef _WIN32
	ph->thread = CreateThread(NULL,0,recv_thread_func,ph,0,NULL);
	return ph->thread ? 0 : -1;
#else
	if (pthread_create(&ph->thread, NULL, recv_thread_func, ph) != 0) return -1;
	return 0;
#endif
}

int pcap_adapter_stop_recv(void* handle) {
	if (!handle) return -1;
	pcap_handle_t* ph = (pcap_handle_t*)handle;
	ph->running = 0;
#ifdef _WIN32
	if (ph->thread) {
		WaitForSingleObject(ph->thread, 2000);
		CloseHandle(ph->thread);
		ph->thread = NULL;
	}
#else
	pthread_join(ph->thread, NULL);
#endif
	return 0;
}

int pcap_adapter_close(void* handle) {
	if (!handle) return -1;
	pcap_handle_t* ph = (pcap_handle_t*)handle;
	if (ph->running) pcap_adapter_stop_recv(ph);
	if (ph->p) { pcap_close(ph->p); ph->p = NULL; }
	free(ph);
	return 0;
}

#ifdef _WIN32
static DWORD WINAPI recv_thread_func(LPVOID arg) {
	pcap_handle_t* ph = (pcap_handle_t*)arg;
	while (ph->running) {
		pcap_dispatch(ph->p, 10, (pcap_handler)pcap_dispatch_cb, (u_char*)ph);
	}
	return 0;
}
#else
static void* recv_thread_func(void* arg) {
	pcap_handle_t* ph = (pcap_handle_t*)arg;
	while (ph->running) {
		pcap_dispatch(ph->p, 10, (pcap_handler)pcap_dispatch_cb, (u_char*)ph);
	}
	return NULL;
}
#endif
